package com.dbapp.service;

import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONUtil;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @Author lihaibo
 * @Date 2021/3/11 15:37
 * @Version 1.0
 * @Description TODO
 */
@Service
public class CacheService {

    /**
     * redis key生成默认 分隔符
     */
    private String SEPARATOR = ":";

    @Resource
    RedisTemplate<String, Object> redisTemplate;



    // ========================  zset   ==================
    public void zadd(String key, Object value,Double score) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key,value,score);
    }

    public <T> void batchZadd(String key, List<T> values,Double score) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        Set<ZSetOperations.TypedTuple<Object>> set = new LinkedHashSet<>();
        values.forEach(v -> {
            set.add( new DefaultTypedTuple<>(v,score));
        });
        zset.add(key,set);
    }

    public Set<Object> getZset(String key, long offset, long limitCount, long min, long max){
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
       return zset.reverseRangeByScore(key,min,max,offset,limitCount);
    }

    /**
     * 想redis中添加数据
     *
     * @param key
     * @param value
     * @param timeout
     * @param timeUnit
     */
    public void add(String key, Object value, long timeout, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * 查询key的生命周期
     * @param key redis 存数据的键
     * @return 默认单位秒，也可以自行传入单位
     */
    public long getKeyExpire(String key) {
        return redisTemplate.getExpire(key);
    }
    public Boolean hasKey(String key){
       return redisTemplate.hasKey(key);
    }

    /**
     * 想redis中添加数据
     *
     * @param key
     * @param value

     */
    public void add(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 自增1
     * @param key
     */
    public Long incr(String key) {
        return redisTemplate.opsForValue().increment(key);
    }

    /**
     * 自减1
     * @param key
     */
    public Long decr(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }

    /**
     * 从redis中获取指定key的value
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    /**
     * 从redis中获取指定key的value
     *
     * @param key
     * @return
     */
    public <T> T get(String key,Class<T> tClass) {
        Object o = redisTemplate.opsForValue().get(key);
        return (T)o;
    }

    /**
     * 从redis中删除指定key的value
     *
     * @param key
     * @return
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }


    //=============bitmap=================

    public Boolean setbit(String key,int offset,boolean b) {
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        return vo.setBit(key,offset,b);
    }

    public Boolean getbit(String key,int offset) {
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        return vo.getBit(key,offset);
    }

//    public Long bitcount(String key) {
//        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
//    }
//=================hash============================
    public Object hashGet(String key, Object field) {
        HashOperations<String, Object, Object> ho = redisTemplate.opsForHash();
        return ho.get(key,field);
    }

    public void hashPut(String key, Object field,Object value) {
        HashOperations<String, Object, Object> ho = redisTemplate.opsForHash();
        ho.put(key,field,value);
    }

    public void hashPutAll(String key, Map<String,String> map) {
        redisTemplate.opsForHash().putAll(key,map);
    }
    public void hashRemove(String key, Object... fields) {
        HashOperations<String, Object, Object> ho = redisTemplate.opsForHash();
        ho.delete(key,fields);
    }
    public Long hashIncrement(String key, Object field,Long value) {
        HashOperations<String, Object, Object> ho = redisTemplate.opsForHash();
       return ho.increment(key,field,value);
    }

    public boolean hashHasKey(String key, Object field) {
        HashOperations<String, Object, Object> ho = redisTemplate.opsForHash();
        return ho.hasKey(key,field);
    }
    /**
     * 添加or更新hash的值
     *
     * @param key
     * @param field
     * @param value
     */
    public void hset(String key, String field, String value) {
        redisTemplate.opsForHash().put(key, field, value);
    }
    public Map<String, String> hgetall(String key) {
        return redisTemplate.execute((RedisCallback<Map<String, String>>) con -> {
            Map<byte[], byte[]> result = con.hGetAll(key.getBytes());
            if (CollectionUtils.isEmpty(result)) {
                return new HashMap<>(0);
            }

            Map<String, String> ans = new HashMap<>(result.size());
            for (Map.Entry<byte[], byte[]> entry : result.entrySet()) {
                ans.put(new String(entry.getKey()), new String(entry.getValue()).replaceAll("\\\"",""));
            }
            return ans;
        });
    }

    public Map<String, String> hmget(String key, List<String> fields) {
        List<String> result = redisTemplate.<String, String>opsForHash().multiGet(key, fields);
        Map<String, String> ans = new HashMap<>(fields.size());
        int index = 0;
        for (String field : fields) {
            if (result.get(index) == null) {
                continue;
            }
            ans.put(field, result.get(index));
        }
        return ans;
    }

    /**
     * 根据多个参数生成key
     * @param md5 是否开启md5编码
     * @param args
     * @return
     */
    public String generateKey(Boolean md5,String baseKey,Object ... args){
        String sourceKey = Arrays.stream(args).map(JSONUtil::toJsonStr).collect(Collectors.joining(SEPARATOR));
        if(md5){
            sourceKey = MD5.create().digestHex(sourceKey);
        }
        if(ObjectUtils.isEmpty(baseKey)){
            return sourceKey;
        }
        return baseKey+SEPARATOR+sourceKey;
    }

    public String generateKeyForMap(String apiId, Map<String, Object>... mapParams) {

        Map<String,Object> allMap = new HashMap<>();
        Arrays.stream(mapParams).forEach(x->{
            if(!ObjectUtils.isEmpty(x)){
                allMap.putAll(x);
            }
        });


        StringBuilder str = new StringBuilder();

        allMap.keySet().stream().filter(key->!ObjectUtils.isEmpty(key))
                .sorted()
                .forEach(key->{
                    if(!ObjectUtils.isEmpty(allMap.get(key))){
                        str.append(key).append("=").append(allMap.get(key)).append("&");
                    }
                });

        String param=str.substring(0,str.length() - 1);

        return generateKey(true,apiId,param);
    }




}
